<template>
    <div class="wrapper">
        <div class="map" id="map"></div>

        <div class="btnList">
            <el-button type="primary" @click="menuClick('LineString')">测距</el-button>
            <el-button type="primary" @click="menuClick('Circle')">圆形侧面</el-button>
            <el-button type="primary" @click="menuClick('Polygon')">多边形侧面</el-button>
            <el-button type="primary" @click="clear">清空</el-button>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { nanoid } from 'nanoid';
import { Feature, Map, MapBrowserEvent, Overlay, View } from 'ol';
import { getArea, getLength } from 'ol/sphere';
import { FeatureLike } from 'ol/Feature';
import { fromLonLat } from 'ol/proj';
import { Circle, Geometry, LineString, Point } from 'ol/geom';
import { Draw, Snap } from 'ol/interaction';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import { XYZ } from 'ol/source';
import VectorSource from 'ol/source/Vector';
import { Fill, Icon, Stroke, Style, Text } from 'ol/style';
import { onMounted, ref } from 'vue';
import Polygon, { fromCircle } from 'ol/geom/Polygon';

const map = ref();
const source = ref();
const layer = ref();
const uid = ref(nanoid());
const draw = ref();
const snap = ref();
const modify = ref();
const style = new Style({
    fill: new Fill({ color: [255, 128, 0, 0.5] }),
    stroke: new Stroke({ color: [255, 128, 0, 0.5] }),
    image: new Icon({
        src: new URL('../../assets/map-point.png', import.meta.url).href,
        width: 32,
        height: 32
    })
});
const vectorStyle = new Style({
    fill: new Fill({ color: [255, 255, 255, 0.2] }),
    stroke: new Stroke({
        color: [10, 0, 32, 1],
        lineDash: [10, 10],
        width: 2
    })
});
const labelStyle = new Style({
    text: new Text({
        font: '14px Calibri,sans-serif',
        fill: new Fill({ color: 'rgba(255,255,255,1)' }),
        backgroundFill: new Fill({ color: '#f1000a' }),
        padding: [3, 3, 3, 3],
        textBaseline: 'bottom',
        offsetY: -15
    })
});
const segmentStyle = new Style({
    text: new Text({
        font: '14px Calibri,sans-serif',
        fill: new Fill({ color: 'rgba(255,255,255,1)' }),
        backgroundFill: new Fill({ color: '#f1000a' }),
        padding: [3, 3, 3, 3],
        textBaseline: 'bottom',
        offsetY: -15
    })
});

let segmentStyles: Style[] = [];
onMounted(() => {
    segmentStyles = [segmentStyle];
    map.value = new Map({
        target: 'map',
        view: new View({
            center: fromLonLat([116.4, 39.9]),
            zoom: 12,
            projection: 'EPSG:3857'
        }),
        layers: [
            new TileLayer({
                source: new XYZ({
                    url: `http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7`
                })
            })
        ]
    });

    source.value = new VectorSource({ wrapX: true });
    layer.value = new VectorLayer({
        source: source.value,
        style: function (feature: Feature) {
            return styleFunction(feature);
        }
    });
    layer.value.set('uid', uid.value);
    map.value.addLayer(layer.value);

    snap.value = new Snap({
        source: source.value
    });
    map.value.addInteraction(snap.value);

    const feature = new Feature({
        geometry: new Point([116.37829804066813, 39.866640485893114])
    });
    source.value.addFeature(feature);
});

function menuClick(type: 'LineString' | 'Circle' | 'Polygon') {
    draw.value = new Draw({
        type: type,
        source: source.value,
        style: styleFunction
    });
    draw.value.on('drawend', (e) => {
        console.log(e.feature);
        draw.value.setActive(false);
    });
    map.value.addInteraction(draw.value);
}

function styleFunction(feature: FeatureLike) {
    const styles = [vectorStyle];
    const geometry = feature.getGeometry();
    if (!geometry) {
        return;
    }
    let line: LineString | undefined;
    const type = geometry.getType();
    let point: Point | undefined;
    let label: string | undefined;

    if (type === 'LineString') {
        line = geometry as LineString;
        point = new Point(line.getLastCoordinate());
        label = formatLength(line);
    } else if (type === 'Circle') {
        const circle = geometry as Circle;
        const polygon = fromCircle(circle);
        label = formatArea(polygon);
        point = polygon.getInteriorPoint();
    } else if (type === 'Polygon') {
        const polygon = geometry as Polygon;
        point = polygon.getInteriorPoint();
        label = formatArea(polygon);
        line = new LineString(polygon.getCoordinates()[0]);
    }

    if (point && label) {
        labelStyle.setGeometry(point);
        labelStyle.getText()?.setText(label);
        styles.push(labelStyle);
    }

    if (line) {
        let count = 0;
        line.forEachSegment((a, b) => {
            const segment = new LineString([a, b]);
            const label = formatLength(segment);
            if (segmentStyles.length - 1 < count) {
                segmentStyles.push(segmentStyle.clone());
            }
            const segmentPoint = new Point(segment.getCoordinateAt(0.5));
            segmentStyles[count].setGeometry(segmentPoint);
            segmentStyles[count].getText()?.setText(label);
            styles.push(segmentStyles[count]);
            count++;
        });
    }

    return styles;
}

function formatLength(geometry: Geometry) {
    const length = getLength(geometry);
    let output = '';
    if (length > 100) {
        output = Math.round((length / 1000) * 100) / 100 + ' km';
    } else {
        output = Math.round(length * 100) / 100 + 'm';
    }
    return output;
}

function formatArea(geometry: Geometry) {
    const area = getArea(geometry);
    let output = '';
    if (area > 10000) {
        output = Math.round((area / 1000000) * 100) / 100 + ' km²';
    } else {
        output = Math.round(area * 100) / 100 + ' m²';
    }
    return output;
}

function clear() {
    if (modify.value) {
        modify.value.setActive(false);
        map.value.removeInteraction(modify.value);
        modify.value.dispose();
    }
    if (draw.value) {
        map.value.removeInteraction(draw.value);
        draw.value.dispose();
    }
    layer.value.getSource().clear(true);
}
</script>

<style lang="scss" scoped>
.wrapper {
    height: 100%;
    position: relative;
    .map {
        height: 100%;
    }
    .btnList {
        position: absolute;
        top: 60px;
        left: 100px;
    }
}
</style>